package com.qfedu.kbonline.common.core;

import com.qfedu.kbonline.common.model.CodeGenModel;
import com.qfedu.kbonline.common.model.DbModel;
import lombok.extern.slf4j.Slf4j;
import org.beetl.core.Configuration;
import org.beetl.core.GroupTemplate;
import org.beetl.core.Template;
import org.beetl.core.resource.ClasspathResourceLoader;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.sql.*;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.Date;

/**
 * @program: SuperAsvin
 * @description: 代码生成器的核心
 * @author: ErBa(韩智飞)
 * @create: 2020-09-15 00:03
 **/

@Slf4j
public class CodeCreate {

    /**
     * 解析数据库
     * @param model 数据库连接信息
     * @return 对应数据库的所有的表和对应字段和类型*/
    public static Map<String, Map<String,String>> parseTable(DbModel model){
        //jdbc
        try {
            //存储解析的结果：键：表名 ，值：表中对应的：字段，类型
            Map<String, Map<String,String>> map=new LinkedHashMap<>();
            //加载驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
            //1.获取连接对象
            Connection connection= DriverManager.getConnection("jdbc:mysql://"+model.getHost()+":"+model.getPort()+"/"+model.getDbname()+"?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai",
                    model.getUser(),model.getPass()
            );
            //2.获取元数据
            DatabaseMetaData metaData=connection.getMetaData();
            // 定义
            String[] types={ "TABLE" };
            /**
             * getTables 获取数据库中的所有的表或者视图
             * 参数说明：
             * 1.数据库名称
             * 2.用户名
             * 3.表名
             * 4.类型 表(Table)或者视图(View)*/
            //3.获取结果 表名的结果
            ResultSet rs1=metaData.getTables(model.getDbname(),model.getUser(),null,types);
            //4.获取执行SQL的对象
            Statement statement=connection.createStatement();
            while (rs1.next()){
                String tname=rs1.getString("TABLE_NAME");
                System.out.println("表名" + tname);
                //5.根据表名获取字段信息
                ResultSet rs2=statement.executeQuery("select * from `"+tname+"` limit 1");
                //获取表元数据信息
                ResultSetMetaData rsmetadata=rs2.getMetaData();
                //获取字段的数量
                int len=rsmetadata.getColumnCount();
                //存储对应的字段和类型名称
                Map<String,String> fields=new LinkedHashMap<>();
                //表中的字段信息：字段的名称和字段的类型
                for(int i=1;i<=len;i++){
                    fields.put(convertColumn(rsmetadata.getColumnName(i)),convertType(rsmetadata.getColumnType(i)));
                }
                map.put(tname,fields);
            }
            return map;
        } catch (Exception throwables) {
            throwables.printStackTrace();
        }
        return null;
    }

    /*转换数据库的类型为Java中的类型*/
    private static String convertType(int t){
        String type;
        switch (t){
            case -6:
            case 5:
            case 4:
                type="Integer";break;
            case 6:
            case 8:
            case 2:
                type="Double";break;
            case 1:
            case 12:
            case -1:
                type="String";break;
            case 91:
            case 92:
            case 93:
                type="Date";break;
            case 3:
                type="BigDecimal";break;
            default:type="String";break;
        }
        return type;
    }

    //获取当前日期和时间
    private static String getTime(){
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return sdf.format(new Date());
    }
    //数据库字段名转换为实体类属性名 数据库中命名：多个单词 下划线隔开 Java小驼峰式命名
    private static String convertColumn(String name){
        //
        if(name.indexOf('_')>0){
            //包含下划线
            String[] arr=name.split("_");
            StringBuffer buffer=new StringBuffer();
            buffer.append(arr[0]);
            for (int i = 1; i <arr.length ; i++) {
                buffer.append(convertUp(arr[i]));
            }

            return buffer.toString();
        }else {
            //首字母大写 其他小写
            return name;
        }
    }


    //数据库表名转换为类名 数据库中命名：多个单词 下划线隔开 Java大驼峰式命名
    private static String convertClass(String name){
        //
        if(name.indexOf('_')>0){
            //包含下划线
            String[] arr=name.split("_");
            StringBuffer buffer=new StringBuffer();
            for(String s:arr){
                //首字母大写 其他小写
                buffer.append(convertUp(s));
            }
            return buffer.toString();
        }else {
            //首字母大写 其他小写
            return convertUp(name);
        }
    }

    //转换字符串   首字母大写 其他小写
    private static String convertUp(String n){
        if(n!=null && n.length()>0){
            if(n.length()==1){
                return n.toUpperCase();
            }else {
                return n.substring(0,1).toUpperCase()+n.substring(1).toLowerCase();
            }
        }else {
            return "";
        }
    }
    //转换字符串   首字母小写 其他首字母大写
    private static String convertLower(String n){
        if(n!=null && n.length()>0){
            return n.substring(0,1).toLowerCase()+n.substring(1);
        }else {
            return "";
        }
    }
    //创建包对应的文件夹
    private static String createDir(String pkname){
        String ppname = "src.main.java.com.qfedu.kbonline";
        String path =ppname.replaceAll("\\.","/");
        //生成模板代码  beetl 模板引擎 解析模板 根据模板生成指定的代码
        System.out.println("项目路径 ：" + path);
        File file=new File(path);
        //如果文件夹不存在 创建 包 其实就是文件夹
        if(!file.exists()){
            file.mkdirs();
        }
        String[] arr={"entity","dao","service","service/impl","controller","vo"};
        for(String s:arr){
            File child=new File(file,s);
            if(!child.exists()){
                child.mkdirs();
            }
        }
        return path;
    }
    //生成新增语句
    private static String createInsertSQL(String tname, Set<String> keys){
        StringBuffer buffer=new StringBuffer();
        buffer.append("insert into "+tname+" (");
        for(String s:keys){
            buffer.append(s+",");
        }
        buffer.deleteCharAt(buffer.length()-1);
        buffer.append(") values(");
        for(String s:keys){
            buffer.append("#{"+s+"},");
        }
        buffer.deleteCharAt(buffer.length()-1);
        buffer.append(")");
        return buffer.toString();
    }
    /**
     * 根据模板生成代码 注解版本
     * 实体层
     * 持久层
     * 业务层
     * 业务实现层
     * 控制层
     * @param genModel 生成信息 对应的包、注释的内容
     * @param dbModel 数据库连接信息
     **/
    public static boolean createCode(CodeGenModel genModel, DbModel dbModel){
        //解析数据库 获取表信息
        Map<String,Map<String,String>>  map=parseTable(dbModel);
        if(map!=null && map.size()>0){
            //根据包创建目录
            String basePath=createDir(genModel.getPkname());
            //每个都需要的内容
            Map<String,String> defaultMap=new HashMap<>();
            defaultMap.put("author",genModel.getAuthor());
            defaultMap.put("desc",genModel.getDes());
            defaultMap.put("ctime",getTime());
            //指定模板的目录
            ClasspathResourceLoader resourceLoader = new ClasspathResourceLoader("com/qfedu/kbonline/common/temp");
            try {
                //获取模板引擎默认配置
                Configuration cfg= Configuration.defaultConfiguration();
                //获取模板的组
                GroupTemplate gt = new GroupTemplate(resourceLoader, cfg);
                //遍历所有表
                for(String tname :map.keySet()) {
                    //1.生成实体
                    Template tentiy = gt.getTemplate("/EntityTemp.btl");
                    Map<String, Object> mapEntity = new HashMap<>();
                    mapEntity.putAll(defaultMap);
                    mapEntity.put("pkname", genModel.getPkname() + ".entity");
                    mapEntity.put("extrapk", "import java.util.Date;");
                    mapEntity.put("entityname", convertClass(tname));
                    mapEntity.put("fields", map.get(tname));
                    //生成源码文件
                    tentiy.binding(mapEntity);
                    System.out.println(mapEntity);
                    tentiy.renderTo(new FileOutputStream(basePath+"/entity/"+mapEntity.get("entityname")+".java"));
                    //2.生成持久类
                    Template tdao = gt.getTemplate("/DaoTemp.btl");
                    Map<String, Object> mapDao = new HashMap<>();
                    mapDao.putAll(defaultMap);
                    mapDao.put("pkname", genModel.getPkname() + ".dao");
                    mapDao.put("pkentiity", mapEntity.get("pkname") +"." + mapEntity.get("entityname"));
                    mapDao.put("entityname", convertClass(tname)+"Dao");
                    mapDao.put("entity", convertClass(tname));
                    mapDao.put("entityparam", convertLower(convertClass(tname)));
                    mapDao.put("allresult", convertClass(tname));
                    mapDao.put("insertsql", createInsertSQL(tname,map.get(tname).keySet()));
                    mapDao.put("deletesql", "delete from "+tname+" where id=#{id}");
                    mapDao.put("selectallsql", "select * from "+tname);
                    //生成源码文件
                    tdao.binding(mapDao);
                    tdao.renderTo(new FileOutputStream(basePath+"/dao/"+mapDao.get("entityname")+".java"));
                    //3.生成业务类
                    //4.生成业务实现类
                    //5.生成控制类

                }
                return true;
            } catch (IOException e) {
                e.printStackTrace();
                return false;
            }

        }else {
            return false;
        }
    }

}
